package com.gitee.qdbp.general.system.web.shiro.simple;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.SubjectContext;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.gitee.qdbp.able.exception.ServiceException;
import com.gitee.qdbp.able.result.ResponseMessage;
import com.gitee.qdbp.base.controlling.IOperateRecordControlling;
import com.gitee.qdbp.base.shiro.mgt.OnlineManager;
import com.gitee.qdbp.base.system.model.IAccount;
import com.gitee.qdbp.base.system.model.IAccountType;
import com.gitee.qdbp.base.system.service.IUserTypeConverter;
import com.gitee.qdbp.base.utils.SessionTools;
import com.gitee.qdbp.general.system.api.personnel.model.UserCoreBean;
import com.gitee.qdbp.general.system.api.personnel.model.UserCoreWhere;
import com.gitee.qdbp.general.system.api.personnel.model.VirtualUser;
import com.gitee.qdbp.general.system.api.personnel.service.IUserCoreQueryer;
import com.gitee.qdbp.general.system.error.UserErrorCode;

/**
 * RememberMeCookie的保存和恢复
 *
 * @author zhaohuihua
 * @version 160802
 */
public class SimpleRememberMeManager extends CookieRememberMeManager {

    private static final Logger log = LoggerFactory.getLogger(SimpleRememberMeManager.class);

    @Autowired(required = false)
    private IOperateRecordControlling operateRecord;
    @Autowired
    private IUserCoreQueryer userCoreQueryer;
    @Autowired
    private IUserTypeConverter userTypeConverter;
    @Autowired(required = false)
    private OnlineManager onlineManager;
    protected boolean clearAuthorizationInfo = true;
    @Autowired(required = false)
    private OnAuthEventHandler onLoginSuccessHandler;

    /** 登录成功之后的处理 **/
    protected final void onAuthenticatedSuccess(IAccount user) throws ServiceException {
        if (onLoginSuccessHandler != null) {
            onLoginSuccessHandler.onAuthenticatedSuccess(user);
        }
    }

    @Override
    protected PrincipalCollection getIdentityToRemember(Subject subject, AuthenticationInfo info) {
        PrincipalCollection principals = info.getPrincipals();
        if (principals instanceof SimplePrincipalCollection) {
            // 登录信息, 只将关键信息保存到cookie中
            SimplePrincipalCollection spc = (SimplePrincipalCollection) principals;
            LoginUser logined = (LoginUser) spc.getPrimaryPrincipal();

            CookieUser cu = new CookieUser();
            cu.setId(logined.getId());
            cu.setFactory(logined.getTenantCode());
            cu.setType(logined.getUserType().name());
            SimplePrincipalCollection newer = new SimplePrincipalCollection();
            newer.add(cu, logined.toString());
            return newer;
        } else {
            return principals;
        }
    }

    protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {

        PrincipalCollection principals = super.convertBytesToPrincipals(bytes, subjectContext);
        if (principals instanceof SimplePrincipalCollection) {
            SimplePrincipalCollection spc = (SimplePrincipalCollection) principals;

            IAccount logined;
            try {
                logined = convertCookieUserToPrincipals(spc);
            } catch (ServiceException e) {
                throw new AuthenticationException("Failed to query user info by id.", e);
            }
            if (logined == null) {
                return null;
            }

            try {
                onAuthenticatedSuccess(logined);
            } catch (ServiceException e) {
                throw new AuthenticationException("Failed to call onLoginSuccess.", e);
            }

            // 清除授权信息(新用户可能在redis之类的缓存中已存在授权信息)
            if (clearAuthorizationInfo && onlineManager != null) {
                onlineManager.clearAuthorizationInfo(logined.getId());
            }

            SimplePrincipalCollection newer = new SimplePrincipalCollection();
            newer.add(logined, logined.toString());
            return newer;
        } else {
            return principals;
        }
    }

    protected IAccount convertCookieUserToPrincipals(SimplePrincipalCollection spc) throws ServiceException {
        // 从cookie中读取userId
        // 然后从数据库查询对应数据, 恢复登录信息
        CookieUser cu = (CookieUser) spc.getPrimaryPrincipal();
        IAccount older = SessionTools.checkLoginUser();
        if (older != null && older.getId().equals(cu.getId())) {
            log.trace("User already logined: {}, userId={}.", older.toString(), cu.getId());
            return null;
        }

        if (operateRecord != null) {
            operateRecord.start();
        }
        IAccountType userType = userTypeConverter.convert(cu.getType());
        UserCoreBean user;
        try {
            UserCoreWhere where = new UserCoreWhere();
            where.setTenantCode(cu.getFactory());
            where.setUserType(userType);
            where.setId(cu.getId());
            user = userCoreQueryer.find(where);
            if (user == null) {
                throw new ServiceException(UserErrorCode.USERINFO_LOGIN_ERROR);
            }
            user.setPassword(null);

            LoginUser logined = new LoginUser(user);

            ResponseMessage returns = new ResponseMessage(user);
            if (operateRecord != null) {
                operateRecord.save(this.getClass(), "convertCookieUserToPrincipals", "自动登录", returns, logined);
            }
            return logined;
        } catch (ServiceException e) {
            ResponseMessage returns = new ResponseMessage(e);
            VirtualUser operator = new VirtualUser();
            operator.setTenantCode(cu.getFactory());
            operator.setUserType(userType);
            operator.setId(cu.getId());
            operator.setNickName("{未知}");
            if (operateRecord != null) {
                operateRecord.save(this.getClass(), "convertCookieUserToPrincipals", "自动登录", returns, operator);
            }
            throw e;
        }

    }
}
